package org.example.distributedlock.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.example.distributedlock.mapper.StockMapper;
import org.example.distributedlock.pojo.StockDb;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
//@Scope(value = "prototype",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class StockService {

    @Autowired
    private StockMapper stockMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

//    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT_KEY;
//
//    static {
//        UNLOCK_SCRIPT_KEY = new DefaultRedisScript<>();
//        UNLOCK_SCRIPT_KEY.setLocation(new ClassPathResource("isEventLock.lua"));
//        UNLOCK_SCRIPT_KEY.setResultType(Long.class);
//    }

    /**
     * 使用lua 脚本处理防止误删Key 操作
     */
    public void deduct() {
        String uuid = UUID.randomUUID().toString();
        // 简单分布式锁
        while (Boolean.FALSE.equals(redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS))) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            deduct();
        }
        try {
            // 获取库存
            String stock = redisTemplate.opsForValue().get("stock");
            // 判断库存是否满足
            if (stock != null && !stock.isEmpty()) {
                // 转换库存为数字类型
                int st = Integer.parseInt(stock);
                if (st > 0) {
                    redisTemplate.opsForValue().set("stock", String.valueOf(--st));
                }
            }
        } finally {
            String lock = "if redis.call('get',KEYS[1]) == ARGV[1] " +
                    "then" +
                    " return redis.call('del',KEYS[1])" +
                    "else" +
                    " return 0 " +
                    "end";
            redisTemplate.execute(new DefaultRedisScript<>(lock,Boolean.class), Arrays.asList("lock"), uuid);
        }
    }

    /**
     * 防死锁 改为 循环方式 重试获取锁
     * 同时也要方式锁误删
     * 例如：用户都使用同一把锁，A 任务执行完成 锁释放,但另外用户未完成任务页释放锁，就存在锁释放冲突，导致业务问题
     */
    public void deduct5() {
        String uuid = UUID.randomUUID().toString();
        // 简单分布式锁
        while (Boolean.FALSE.equals(redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS))) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            deduct();
        }
        try {
            // 获取库存
            String stock = redisTemplate.opsForValue().get("stock");
            // 判断库存是否满足
            if (stock != null && !stock.isEmpty()) {
                // 转换库存为数字类型
                int st = Integer.parseInt(stock);
                if (st > 0) {
                    redisTemplate.opsForValue().set("stock", String.valueOf(--st));
                }
            }
        } finally {
            // 非原子性操作，可能存在锁超时自动释放情况
            // 可采用lua脚本操作处理
            if (Objects.equals(redisTemplate.opsForValue().get("lock"), uuid)) {
                // 解锁
                redisTemplate.delete("lock");
            }
        }
    }

    /**
     * 防死锁 增加锁自动过期时间
     */
    public void deduct4() {
        // 简单分布式锁
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111", 3, TimeUnit.SECONDS);
        if (!lock) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            deduct();
        } else {
            try {
                // 获取库存
                String stock = redisTemplate.opsForValue().get("stock");
                // 判断库存是否满足
                if (stock != null && !stock.isEmpty()) {
                    // 转换库存为数字类型
                    int st = Integer.parseInt(stock);
                    if (st > 0) {
                        redisTemplate.opsForValue().set("stock", String.valueOf(--st));
                    }
                }
            } finally {
                // 解锁
                redisTemplate.delete("lock");
            }
        }
    }

    /**
     * 增加redis 乐观锁
     * watch
     * m
     */
    public void deduct3() {
        redisTemplate.execute(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                // 监听
                operations.watch("stock");
                // 获取库存
                String stock = (String) operations.opsForValue().get("stock");
                // 判断库存是否满足
                if (stock != null && !stock.isEmpty()) {
                    // 开启事务
                    operations.multi();
                    // 转换库存为数字类型
                    int st = Integer.parseInt(stock);
                    // 扣减库存
                    operations.opsForValue().set("stock", String.valueOf(--st));

                    List exec = operations.exec();
                    if (exec != null && exec.size() > 0) {
                        return exec;
                    } else {
                        try {
                            Thread.sleep(40);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        deduct();
                    }
                }
                return null;
            }
        });
    }

    /**
     * 乐观锁
     * 设置最大重试次数
     * 如果采用直接递归由于速度快，需要线程睡眠等待
     */
    public void deduct2() {
        int retryCount = 0;
        int maxRetry = 3;  // 最大重试次数
        boolean updateSuccess = false;

        QueryWrapper<StockDb> productCode = new QueryWrapper<StockDb>().eq("product_code", "1001");

        StockDb stockDb = stockMapper.selectOne(productCode);

        do {
            if (stockDb != null && stockDb.getCount() > 0) {
                int version = stockDb.getVersion();

                stockDb.setCount(stockDb.getCount() - 1);
                stockDb.setVersion(stockDb.getVersion() + 1);

                int updateCount = stockMapper.update(stockDb, new UpdateWrapper<StockDb>().eq("product_code", stockDb.getProductCode()).eq("version", version));

                if (updateCount <= 0) {
                    updateSuccess = true;
//                try {
//                    Thread.sleep(50);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                deduct();
                }
                retryCount++;
            }
            if (!updateSuccess) {
                if (retryCount < maxRetry) {
                    deduct();
                }
            }
        } while (!updateSuccess && retryCount < maxRetry);
    }

    /**
     * 悲观锁方式 使用for Update 行锁处理 配合事务
     */
//    @Transactional
//    public void deduct() {
//        StockDb stockDb = stockMapper.selectStock("1001");
//
//        if (stockDb != null && stockDb.getCount() > 0) {
//            int count = stockDb.getCount();
//            log.warn("原库存 {},现库存 {}", count, stockDb.getCount() - 1);
//            stockDb.setCount(stockDb.getCount() - 1);
//            stockMapper.updateById(stockDb);
//        }
//
//    }


    /**
     * 一条sql 并发由数据库直接处理
     */
//    public void deduct() {
//        stockMapper.deductStock("1001", 1);
//    }


    /**
     * 针对多实例部署 JVM锁失效
     * 1、多实例 失效
     * 2、多例模式 锁失效
     * 3、事务模式下多实例失效
     */
    private ReentrantLock reentrantLock = new ReentrantLock();

//    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
//    public void deduct() {
//        reentrantLock.lock();
//        try {
//            QueryWrapper<StockDb> productCode = new QueryWrapper<StockDb>().eq("product_code", "1001");
//            StockDb stockDb = stockMapper.selectOne(productCode);
//
//            if (stockDb != null && stockDb.getCount() > 0) {
//                int count = stockDb.getCount();
//
//                log.warn("原库存 {},现库存 {}", count, stockDb.getCount() - 1);
//
//                stockDb.setCount(stockDb.getCount() - 1);
//
//                stockMapper.updateById(stockDb);
//            }
//        } finally {
//            reentrantLock.unlock();
//        }
//    }

}
